#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Git部分克隆和推送工具
功能：从源仓库克隆指定目录，推送到目标仓库的相同目录
"""

import os
import subprocess
import shutil
import tempfile
from pathlib import Path


class GitPartialClone:
    def __init__(self):
        self.temp_dir = None
    
    def run_command(self, command, cwd=None):
        """执行命令并返回结果"""
        try:
            result = subprocess.run(
                command, 
                shell=True, 
                cwd=cwd, 
                capture_output=True, 
                text=True, 
                encoding='utf-8'
            )
            if result.returncode != 0:
                print(f"命令执行失败: {command}")
                print(f"错误信息: {result.stderr}")
                return False, result.stderr
            return True, result.stdout
        except Exception as e:
            print(f"执行命令时发生异常: {e}")
            return False, str(e)
    
    def clone_partial_directory(self, source_repo, target_path, branch='main'):
        """使用sparse-checkout克隆指定目录"""
        print(f"开始克隆 {source_repo} 的 {target_path} 目录...")
        
        # 创建临时目录
        self.temp_dir = tempfile.mkdtemp()
        print(f"临时目录: {self.temp_dir}")
        
        try:
            # 1. 克隆仓库（不检出文件）
            success, output = self.run_command(
                f"git clone --no-checkout {source_repo} .", 
                cwd=self.temp_dir
            )
            if not success:
                return False
            
            # 2. 检查可用分支并选择合适的分支
            success, output = self.run_command(
                "git branch -r", 
                cwd=self.temp_dir
            )
            if success:
                branches = output.strip().split('\n')
                available_branches = [b.strip().replace('origin/', '') for b in branches if 'origin/' in b]
                print(f"可用分支: {available_branches}")
                
                # 如果指定的分支不存在，尝试使用master或其他分支
                if branch not in available_branches:
                    if 'master' in available_branches:
                        branch = 'master'
                        print(f"切换到master分支")
                    elif available_branches:
                        branch = available_branches[0]
                        print(f"切换到分支: {branch}")
            
            # 3. 启用sparse-checkout
            success, output = self.run_command(
                "git config core.sparseCheckout true", 
                cwd=self.temp_dir
            )
            if not success:
                return False
            
            # 4. 设置要检出的路径
            sparse_checkout_file = os.path.join(self.temp_dir, '.git', 'info', 'sparse-checkout')
            os.makedirs(os.path.dirname(sparse_checkout_file), exist_ok=True)
            with open(sparse_checkout_file, 'w', encoding='utf-8') as f:
                f.write(f"{target_path}/*\n")
                # 也尝试包含单个文件
                f.write(f"{target_path}\n")
            
            # 5. 检出指定分支和路径
            success, output = self.run_command(
                f"git checkout {branch}", 
                cwd=self.temp_dir
            )
            if not success:
                return False
            
            print(f"成功克隆 {target_path} 目录")
            return True
            
        except Exception as e:
            print(f"克隆过程中发生错误: {e}")
            return False
    
    def push_to_target_repo(self, target_repo, target_path, commit_message="Update from partial clone"):
        """将克隆的文件推送到目标仓库"""
        print(f"开始推送到目标仓库 {target_repo}...")
        
        # 创建目标仓库的临时目录
        target_temp_dir = tempfile.mkdtemp()
        
        try:
            # 1. 克隆目标仓库
            success, output = self.run_command(
                f"git clone {target_repo} .", 
                cwd=target_temp_dir
            )
            if not success:
                return False
            
            # 2. 创建目标路径（如果不存在）
            target_full_path = os.path.join(target_temp_dir, target_path)
            os.makedirs(target_full_path, exist_ok=True)
            
            # 3. 复制文件
            source_full_path = os.path.join(self.temp_dir, target_path)
            if os.path.exists(source_full_path):
                # 删除目标目录中的现有文件
                if os.path.exists(target_full_path):
                    shutil.rmtree(target_full_path)
                
                # 复制新文件
                shutil.copytree(source_full_path, target_full_path)
                print(f"文件已复制到 {target_full_path}")
            else:
                print(f"源路径不存在: {source_full_path}")
                return False
            
            # 4. 添加文件到git
            success, output = self.run_command(
                f"git add {target_path}", 
                cwd=target_temp_dir
            )
            if not success:
                return False
            
            # 5. 检查是否有变更
            success, output = self.run_command(
                "git status --porcelain", 
                cwd=target_temp_dir
            )
            if not success:
                return False
            
            if not output.strip():
                print("没有检测到变更，无需提交")
                return True
            
            # 6. 提交变更
            success, output = self.run_command(
                f'git commit -m "{commit_message}"', 
                cwd=target_temp_dir
            )
            if not success:
                return False
            
            # 7. 推送到远程仓库
            success, output = self.run_command(
                "git push origin main", 
                cwd=target_temp_dir
            )
            if not success:
                # 尝试推送到master分支
                success, output = self.run_command(
                    "git push origin master", 
                    cwd=target_temp_dir
                )
                if not success:
                    return False
            
            print("成功推送到目标仓库")
            return True
            
        except Exception as e:
            print(f"推送过程中发生错误: {e}")
            return False
        finally:
            # 清理目标临时目录
            if os.path.exists(target_temp_dir):
                shutil.rmtree(target_temp_dir)
    
    def cleanup(self):
        """清理临时文件"""
        if self.temp_dir and os.path.exists(self.temp_dir):
            try:
                # 在Windows上，有时需要修改文件权限才能删除
                if os.name == 'nt':  # Windows
                    import stat
                    def handle_remove_readonly(func, path, exc):
                        if os.path.exists(path):
                            os.chmod(path, stat.S_IWRITE)
                            func(path)
                    shutil.rmtree(self.temp_dir, onerror=handle_remove_readonly)
                else:
                    shutil.rmtree(self.temp_dir)
                print("临时文件已清理")
            except Exception as e:
                print(f"清理临时文件时发生错误: {e}")
                print(f"请手动删除临时目录: {self.temp_dir}")
    
    def transfer_directory(self, source_repo, target_repo, directory_path, 
                          source_branch='main', commit_message=None):
        """完整的目录传输流程"""
        if commit_message is None:
            commit_message = f"Update {directory_path} from {source_repo}"
        
        try:
            # 1. 克隆源仓库的指定目录
            if not self.clone_partial_directory(source_repo, directory_path, source_branch):
                print("克隆失败")
                return False
            
            # 2. 推送到目标仓库
            if not self.push_to_target_repo(target_repo, directory_path, commit_message):
                print("推送失败")
                return False
            
            print("目录传输完成！")
            return True
            
        finally:
            self.cleanup()


def main():
    """主函数 - 示例用法"""
    # 配置参数
    source_repo = "https://github.com/username/test.git"  # 源仓库URL
    target_repo = "https://github.com/username/test1.git"  # 目标仓库URL
    directory_path = "people/student/computer"  # 要传输的目录路径
    
    # 创建克隆器实例
    cloner = GitPartialClone()
    
    # 执行传输
    success = cloner.transfer_directory(
        source_repo=source_repo,
        target_repo=target_repo,
        directory_path=directory_path,
        source_branch='main',  # 可以指定分支
        commit_message=f"Sync {directory_path} from source repository"
    )
    
    if success:
        print("✅ 目录同步成功！")
    else:
        print("❌ 目录同步失败！")


if __name__ == "__main__":
    main()